//
// Created by hujin on 2023/3/9.
//

#include <cosi/global_stats_stl.h>
#include <algorithm>
template<typename T, typename I>
T GlobalStatsSTL<T, I>::query(I es, I et) {

    return removed_ids.find(es) == removed_ids.end() ||
           removed_ids.find(et) == removed_ids.end() ? 0 : A[remap_ids[et]][remap_ids[es]];
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::recycle(I node) {
    removed_ids.insert(node);
    A[node].swap(std::unordered_map<I, T>());
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::combine(I id1, I id2, I id_new) {
    id1 = remap_ids[id1];
    id2 = remap_ids[id2];
    if(A.capacity() <= id_new){
        A.reserve(id_new > 10 ? 10: id_new * 2);
    }
    if(remap_ids.capacity() <= id_new){
        remap_ids.reserve(id_new > 10 ? 10: id_new * 2);
    }
    if(real_ids.capacity() < id_new){
        real_ids.reserve(id_new > 10 ? 10: id_new * 2);
    }

    //real_ids' size is constant.

//    T removed = GlobalStats<T, I>::biquery(id1, id2);
//    T tot = sum_in[id1] + sum_in[id2];


    if (A[id1].size() < A[id2].size()) std::swap(id1, id2);
//    A[id_new].swap(A[id1]);
    remap_ids[id_new] = id1;
    real_ids[id1] = id_new;
    real_ids[id_new] = id_new;

    //insert the low rank row to high rank row
    for(auto &x : A[id2]){
        A[id1][x.first] += x.second;
        A[x.first].erase(id2);
        A[x.first][id1] += x.second;
    }

    A[id1].erase(id2);
    A[id1].erase(id1);

//    if (1 - removed / tot < sum_in_tol){
//        sum_in[id_new] = 0;
//        for(auto &x : A[id_new]){
//            sum_in[id_new] += x.second;
//        }
//    } else {
//        sum_in[id_new] = tot - removed;
//    }

//    recycle(id1);
    recycle(id2);
}



template<typename T, typename I>
void GlobalStatsSTL<T, I>::copy(I id1, I id_new) {
    if(A.capacity() <= id_new){
        A.reserve(id_new > 10 ? 10: id_new * 2);
    }
    std::copy(A[remap_ids[id1]].begin(),A[remap_ids[id1]].end(),std::back_inserter(A[id_new]));
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::remove(I id_s, I id_t) {
//    T removed = A[id_t][id_s];
    A[remap_ids[id_t]].erase(remap_ids[id_s]);
//    T tot = sum_in[id_t];
    //todo : maintain in other
//    if (1 - removed / tot < sum_in_tol){
//        sum_in[id_t] = 0;
//        for(auto &x : A[id_t]){
//            sum_in[id_t] += x.second;
//        }
//    } else {
//        sum_in[id_t] = tot - removed;
//    }
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::modify(I id_s, I id_t, T p) {
    A[remap_ids[id_t]][remap_ids[id_s]] += p;
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::iterate_neighbor(I id, std::function<void(I, T)> func) {
    id = remap_ids[id];
    auto &ids = real_ids;
    std::for_each(A[id].cbegin(), A[id].cend(), [&func, &ids](std::pair<I, T> &p){func(ids[p.first], p.second);});
}

template<typename T, typename I>
void GlobalStatsSTL<T, I>::query_sum_in(I id_t) {
    T ret = 0;
    for(auto &x : A[id_t]){
        ret += x.second;
    }
    return ret;
}